﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace UtilZ.Dotnet.Ex.DotnetAdapter
{
    /// <summary>
    /// 
    /// </summary>
    public class WebHelper
    {
        private class UrlDecoder
        {
            private int _bufferSize;

            private int _numChars;

            private char[] _charBuffer;

            private int _numBytes;

            private byte[] _byteBuffer;

            private Encoding _encoding;

            private void FlushBytes()
            {
                if (_numBytes > 0)
                {
                    _numChars += _encoding.GetChars(_byteBuffer, 0, _numBytes, _charBuffer, _numChars);
                    _numBytes = 0;
                }
            }

            internal UrlDecoder(int bufferSize, Encoding encoding)
            {
                _bufferSize = bufferSize;
                _encoding = encoding;
                _charBuffer = new char[bufferSize];
            }

            internal void AddChar(char ch)
            {
                if (_numBytes > 0)
                {
                    FlushBytes();
                }

                _charBuffer[_numChars++] = ch;
            }

            internal void AddByte(byte b)
            {
                if (_byteBuffer == null)
                {
                    _byteBuffer = new byte[_bufferSize];
                }

                _byteBuffer[_numBytes++] = b;
            }

            internal string GetString()
            {
                if (_numBytes > 0)
                {
                    FlushBytes();
                }

                if (_numChars > 0)
                {
                    return new string(_charBuffer, 0, _numChars);
                }

                return string.Empty;
            }
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="encodedValue"></param>
        /// <returns></returns>
        public static string UrlDecode(string encodedValue)
        {
            if (encodedValue == null)
            {
                return null;
            }

            return UrlDecodeInternal(encodedValue, Encoding.UTF8);
        }

        private static string UrlDecodeInternal(string value, Encoding encoding)
        {
            if (value == null)
            {
                return null;
            }

            int length = value.Length;
            UrlDecoder urlDecoder = new UrlDecoder(length, encoding);
            for (int i = 0; i < length; i++)
            {
                char c = value[i];
                switch (c)
                {
                    case '+':
                        c = ' ';
                        break;
                    case '%':
                        if (i < length - 2)
                        {
                            int num = HexToInt(value[i + 1]);
                            int num2 = HexToInt(value[i + 2]);
                            if (num >= 0 && num2 >= 0)
                            {
                                byte b = (byte)((num << 4) | num2);
                                i += 2;
                                urlDecoder.AddByte(b);
                                continue;
                            }
                        }

                        break;
                }

                if ((c & 0xFF80) == 0)
                {
                    urlDecoder.AddByte((byte)c);
                }
                else
                {
                    urlDecoder.AddChar(c);
                }
            }

            return urlDecoder.GetString();
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="encodedValue"></param>
        /// <param name="offset"></param>
        /// <param name="count"></param>
        /// <returns></returns>
        public static byte[] UrlDecodeToBytes(byte[] encodedValue, int offset, int count)
        {
            return UrlDecodeInternal(encodedValue, offset, count);
        }



        private static byte[] UrlDecodeInternal(byte[] bytes, int offset, int count)
        {
            if (!ValidateUrlEncodingParameters(bytes, offset, count))
            {
                return null;
            }

            int num = 0;
            byte[] array = new byte[count];
            for (int i = 0; i < count; i++)
            {
                int num2 = offset + i;
                byte b = bytes[num2];
                switch (b)
                {
                    case 43:
                        b = 32;
                        break;
                    case 37:
                        if (i < count - 2)
                        {
                            int num3 = HexToInt((char)bytes[num2 + 1]);
                            int num4 = HexToInt((char)bytes[num2 + 2]);
                            if (num3 >= 0 && num4 >= 0)
                            {
                                b = (byte)((num3 << 4) | num4);
                                i += 2;
                            }
                        }

                        break;
                }

                array[num++] = b;
            }

            if (num < array.Length)
            {
                byte[] array2 = new byte[num];
                Array.Copy(array, array2, num);
                array = array2;
            }

            return array;
        }

        private static int HexToInt(char h)
        {
            if (h < '0' || h > '9')
            {
                if (h < 'a' || h > 'f')
                {
                    if (h < 'A' || h > 'F')
                    {
                        return -1;
                    }

                    return h - 65 + 10;
                }

                return h - 97 + 10;
            }

            return h - 48;
        }

        private static bool ValidateUrlEncodingParameters(byte[] bytes, int offset, int count)
        {
            if (bytes == null && count == 0)
            {
                return false;
            }

            if (bytes == null)
            {
                throw new ArgumentNullException("bytes");
            }

            if (offset < 0 || offset > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("offset");
            }

            if (count < 0 || offset + count > bytes.Length)
            {
                throw new ArgumentOutOfRangeException("count");
            }

            return true;
        }
    }
}
